#include "alloc.h"

using namespace sfc;

static constexpr auto MIN_ALIGN = sizeof(void*);

extern "C" auto malloc(usize size) -> void*;
extern "C" auto calloc(usize nmemb, usize size) -> void*;
extern "C" auto aligned_alloc(usize align, usize size) -> void*;
extern "C" void free(void*);
extern "C" auto realloc(void* ptr, usize size) -> void*;

namespace sfc::alloc {

auto System::alloc(Layout layout) -> void* {
  if (layout.size == 0) {
    return nullptr;
  }
  if (layout.align <= MIN_ALIGN) {
    return ::malloc(layout.size);
  }
  return ::aligned_alloc(layout.align, layout.size);
}

auto System::alloc_zeroed(Layout layout) -> void* {
  if (layout.align <= MIN_ALIGN) {
    return ::calloc(layout.size / layout.align, layout.align);
  }
  const auto p = System::alloc(layout);
  if (p != nullptr) {
    __builtin_memset(p, 0, layout.size);
  }
  return p;
}

void System::dealloc(void* p, [[maybe_unused]] Layout layout) {
  if (p == nullptr) {
    return;
  }
  ::free(p);
}

auto System::realloc(void* src_ptr, Layout layout, usize new_size) -> void* {
  const auto new_layout = Layout::from_size_align(new_size, layout.align);
  if (src_ptr == nullptr) {
    return System::alloc(new_layout);
  }
  if (new_size == 0) {
    System::dealloc(src_ptr, layout);
    return nullptr;
  }
  if (layout.align <= MIN_ALIGN) {
    return ::realloc(src_ptr, new_size);
  }
  const auto dst_ptr = System::alloc(new_layout);
  __builtin_memcpy(dst_ptr, src_ptr, layout.size);
  return dst_ptr;
}

}  // namespace sfc::alloc
